\section{元编程}

\subsection{makefiles}
\begin{questionbox}
大多数的 makefiles 都提供了 一个名为 clean 的构建目标，这并不是说我们会生成一个名为 clean 的文件，而是我们可以使用它清理文件，让 make 重新构建。您可以理解为它的作用是“撤销”所有构建步骤。在上面的 makefile 中为 paper.pdf 实现一个 clean 目标。您需要将构建目标设置为 phony。您也许会发现 git ls-files 子命令很有用。其他一些有用的 make 构建目标可以在 这里 找到；
\end{questionbox}

为了解决这个问题我们可以在系统中创建一个\texttt{Makefile}文件，并编辑保存。文件内容如下：
\begin{lstlisting}
# 自动创建 paper.tex（如果不存在）
paper.tex:
	touch paper.tex

# paper.pdf 依赖于 paper.tex
paper.pdf: paper.tex
	touch paper.pdf

clean:
	rm -f paper.pdf paper.tex *.aux *.log *.out *.bbl *.blg *.toc

.PHONY: clean
\end{lstlisting}
保存完后我们就可以使用下面这些命令来一步一步地验证\texttt{make}的行为。
\begin{lstlisting}
touch paper.tex
make
make
touch paper.tex
make
make clean
\end{lstlisting}

具体操作和结果如图\ref{makefile操作和结果}
\begin{figure}[H]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-22 154530.png}
	\caption{makefile操作和结果}
	\label{makefile操作和结果}
\end{figure}

在图\ref{makefile操作和结果}中我们可以看到，最开始我们的目录中是没有\texttt{paper}文件的，当我们使用\texttt{make}命令后，目目录中就出现了\texttt{paper}文件，这个时候我们运行\texttt{make clean}文件，会发现\texttt{paper}文件被删除了。

\subsection{Rust 构建系统中的依赖版本管理}

Rust 的构建系统 \texttt{Cargo} 使用 \texttt{Cargo.toml} 文件管理依赖，其版本规范语法清晰且功能强大。这些语法（如 \texttt{\textasciicircum}, \texttt{\textasciitilde}, \texttt{*}, 比较操作符等）在现代包管理器（如 npm、Cargo、Pub 等）中广泛采用。下面我们为每种语法构建具有实际意义的场景，帮助理解其用途。

\subsubsection{尖号语法 \texttt{\textasciicircum1.2.3} —— 向前兼容的默认选择}

\textbf{含义}：允许更新到兼容的版本，即不改变最左边非零数字的版本。
\begin{itemize}
  \item \texttt{\textasciicircum1.2.3} 允许从 \texttt{1.2.3} 到 \texttt{<2.0.0} 的版本（即 \texttt{1.x.x}）
  \item \texttt{\textasciicircum0.2.3} 允许从 \texttt{0.2.3} 到 \texttt{<0.3.0}（即 \texttt{0.2.x}）
  \item \texttt{\textasciicircum0.0.3} 只允许 \texttt{0.0.3}（极不稳定）
\end{itemize}

\subsubsection{波浪号语法 \texttt{\textasciitilde1.2.3} —— 仅补丁更新}

\textbf{含义}：只允许更新最后一个指定的数字位。
\begin{itemize}
  \item \texttt{\textasciitilde1.2.3} 允许从 \texttt{1.2.3} 到 \texttt{<1.3.0}（即 \texttt{1.2.x}）
  \item \texttt{\textasciitilde1.2} 允许从 \texttt{1.2.0} 到 \texttt{<1.3.0}
  \item \texttt{\textasciitilde1} 等同于 \texttt{\textasciicircum1}
\end{itemize}

\subsubsection{通配符 \texttt{*} —— 危险！仅用于原型或锁定版本}

\textbf{含义}：匹配任意版本（非常宽泛，\textbf{不推荐在生产中使用}）。
\begin{itemize}
  \item \texttt{1.*} 表示 \texttt{1.x.x} 任意版本
  \item \texttt{1.2.*} 表示 \texttt{1.2.x} 任意版本
  \item \texttt{*} 表示任意版本
\end{itemize}

\subsubsection{比较操作符 \texttt{>}, \texttt{<}, \texttt{>=}, \texttt{<=}, \texttt{=} —— 精确控制}

\textbf{含义}：使用数学比较来限制版本范围。

\subsubsection{多个版本要求（逗号分隔）—— 组合条件}

\textbf{含义}：用逗号 \texttt{,} 连接多个条件，表示“与”关系。

\subsection{git当做一个简单的CL系统}
\begin{questionbox}
Git 可以作为一个简单的 CI 系统来使用，在任何 git 仓库中的 .git/hooks 目录中，您可以找到一些文件（当前处于未激活状态），它们的作用和脚本一样，当某些事件发生时便可以自动执行。请编写一个 pre-commit 钩子，它会在提交前执行 make paper.pdf 并在出现构建失败的情况拒绝您的提交。这样做可以避免产生包含不可构建版本的提交信息；
\end{questionbox}

为了解决这个问题我们可以在系统中创建一个git仓库，输入如下的代码：
\begin{lstlisting}
mkdir ~/test-repo
cd ~/test-repo
git init
touch paper.tex Makefile
\end{lstlisting}

然后进入hooks仓库并查看
\begin{lstlisting}
    cd .git/hooks
    ls
\end{lstlisting}

然后创建我们的\texttt{pre-commit}命令，代码如下：
\begin{lstlisting}
#!/bin/bash

echo "🔍 正在运行 pre-commit 钩子..."
echo "🚀 正在尝试构建 paper.pdf..."

make paper.pdf

if [ $? -ne 0 ]; then
    echo "❌ 构建失败：paper.pdf 无法生成！"
    echo "🚫 提交被拒绝。请修复错误后再提交。"
    exit 1  

echo "✅ 构建成功：paper.pdf 已生成。"
echo "🎉 提交允许继续。"

exit 0
\end{lstlisting}
保存退出并添加可执行权限。然后输入下面的代码进行测试：
\begin{lstlisting}
chmod +x pre-commit
cd ../..  
echo "Some text" >> paper.tex
git add paper.tex
git commit -m "test pre-commit hook"
\end{lstlisting}

具体操作和结果如图\ref{git当做一个简单的CL系统}
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-22 185114.png}
	\caption{git当做一个简单的CL系统}
	\label{git当做一个简单的CL系统}
\end{figure}

\subsection{Github Page}
\begin{questionbox}
    基于 GitHub Pages 创建任意一个可以自动发布的页面。添加一个 GitHub Action 到该仓库，对仓库中的所有 shell 文件执行 shellcheck(方法之一)；
\end{questionbox}

首先，我在 Gitee 上创建了一个新的公开仓库，命名为 \texttt{my-gitee-pages}，并勾选了“使用 README 初始化仓库”选项。仓库创建完成后，我进入“服务”菜单，选择“Gitee Pages”，将源分支设置为 \texttt{main}，路径为根目录，点击“启动”后，系统自动生成了访问地址 \texttt{https://<用户名>.gitee.io/my-gitee-pages}，实现了静态页面的自动发布。

为了引入自动化质量检查机制，我决定对仓库中的所有 Shell 脚本执行 \texttt{shellcheck}。为此，我创建了一个名为 \texttt{hello.sh} 的测试脚本：

\begin{lstlisting}
#!/bin/bash
echo "Hello from Gitee CI!"
ls -la
\end{lstlisting}

接下来，我配置了 Gitee 的持续集成服务。Gitee 使用 \texttt{.gitee-ci.yml} 文件定义流水线任务。我在项目根目录下创建了该文件，内容如下：

\begin{lstlisting}
version: 2.0

env:
  matrix:
    - OS: ubuntu

jobs:
  shellcheck-job:
    name: Run shellcheck on shell scripts
    runs-on: ${{ OS }}
    steps:
      - name: Checkout code
        uses: gitee-ci/gitee-checkout@master
        with:
          token: ${{ GITEE_TOKEN }}

      - name: Install shellcheck
        run: |
          sudo apt-get update
          sudo apt-get install -y shellcheck

      - name: Find and run shellcheck on all .sh files
        run: |
          find . -name "*.sh" -type f -executable -print0 | xargs -0 shellcheck || exit 1

      - name: Print success
        run: echo "✅ All shell scripts passed shellcheck!"
\end{lstlisting}

该配置文件定义了一个运行在 Ubuntu 环境下的任务，首先检出代码，然后安装 \texttt{shellcheck}，接着查找所有扩展名为 \texttt{.sh} 的可执行文件并进行检查。若发现任何问题，任务将失败，从而阻止潜在的低质量代码被合入主干。

为了确保 CI 流水线能够顺利访问代码，我还配置了私人令牌（Personal Access Token）作为 \texttt{GITEE\_TOKEN} 环境变量，存储在 Gitee CI 的“变量”管理中，并设置为保密状态，以保障安全性。

当我将 \texttt{.gitee-ci.yml} 提交并推送到远程仓库后，Gitee 自动触发了 CI 流水线。我通过“Gitee CI”页面查看执行日志，确认了代码检出、工具安装和脚本检查等步骤均成功完成。为了验证错误检测能力，我故意在 \texttt{hello.sh} 中引入一个未加引号的变量展开：

\begin{lstlisting}
files="*.txt"
ls $files  # 会触发 shellcheck 警告 SC2086
\end{lstlisting}

提交后，CI 任务果然报错，提示“Double quote to prevent globbing and word splitting”，并导致流水线失败。这证明了自动化检查机制的有效性。

最后，我成功构建了一个具备自动发布与静态分析能力的轻量级项目。Gitee Pages 提供了稳定快速的静态托管服务，而 Gitee CI 则实现了代码质量的自动化把关。整个流程无需额外服务器，完全由平台驱动，体现了现代 DevOps 工具链的便捷与高效。这一模式可广泛应用于文档项目、脚本工具库或教学示例中，确保每一次提交都具备可构建性和基本质量保障。

\subsection{构建属于我的github action}

\begin{questionbox}
    构建属于您的 GitHub action，对仓库中所有的 .md 文件执行 proselint 或 write-good，在您的仓库中开启这一功能，提交一个包含错误的文件看看该功能是否生效。
\end{questionbox}

为了进一步提升项目文档的写作质量，我决定在 GitHub 上构建一个自定义的 GitHub Action，用于对仓库中所有的 Markdown 文件（`.md`）执行语言风格检查。我选择了 `write-good` 这一命令行工具，它能够检测英语写作中的冗余表达、被动语态、模糊词汇等问题，非常适合用于技术文档的自动审查。整个流程在 GitHub Actions 中实现，确保每次提交都能自动触发检查。

首先，我在本地项目中创建了一个测试用的 Markdown 文件 \texttt{bad-style.md}，其中故意引入了一些写作风格问题，例如模糊词汇和被动语态：

\begin{lstlisting}
# Sample Document

This document is used to test the write-good action.

It was decided by me that this sentence is too passive and should be avoided.
Also, it is kind of unclear what this paragraph is really about.
Moreover, the usage of "very" and "really" makes it quite weak.
\end{lstlisting}

接下来，我开始构建自定义的 GitHub Action。我创建了目录结构 \texttt{.github/actions/write-good/}，并在其中添加了行为定义文件 \texttt{action.yml}：

\begin{lstlisting}
name: 'Write Good Linter'
description: 'Run write-good on all .md files in the repository'
author: 'me'

inputs:
  pattern:
    description: 'Glob pattern for Markdown files'
    required: false
    default: '**/*.md'

runs:
  using: 'composite'
  steps:
    - name: Install write-good
      shell: bash
      run: |
        npm install -g write-good

    - name: Find and lint Markdown files
      shell: bash
      env:
        PATTERN: ${{ inputs.pattern }}
      run: |
        echo "🔍 Searching for Markdown files matching pattern: $PATTERN"
        found=0
        while IFS= read -r file; do
          if [[ -f "$file" ]]; then
            echo "📝 Checking: $file"
            write-good "$file" --parse
            if [ $? -ne 0 ]; then
              found=1
            fi
          fi
        done < <(find . -name "*.md" -type f ! -path "./node_modules/*" ! -path "./\.git/*")

        if [ $found -eq 1 ]; then
          echo "❌ Some files have style issues detected by write-good."
          exit 1
        else
          echo "✅ All files passed write-good check."
        fi
\end{lstlisting}

该 Action 使用 \texttt{composite} 运行方式，在运行时全局安装 \texttt{write-good}（通过 npm），然后查找所有 \texttt{.md} 文件并逐一检查。若发现任何风格问题，进程将以非零状态退出，从而导致 CI 失败。

随后，我在 \texttt{.github/workflows/lint-writing.yml} 中定义了工作流，以调用这个自定义 Action：

\begin{lstlisting}
name: Check Writing Style

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
  workflow_dispatch:

jobs:
  write-good-check:
    runs-on: ubuntu-latest
    name: Run write-good on Markdown files
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Run write-good linter
        uses: ./.github/actions/write-good
\end{lstlisting}

我将所有更改提交并推送到 GitHub 仓库：

\begin{lstlisting}
git add bad-style.md .github/
git commit -m "Add test markdown and write-good action"
git push origin main
\end{lstlisting}

推送后，我立即进入 GitHub 仓库的 “Actions” 标签页，观察工作流执行情况。CI 任务成功启动，日志显示：

\begin{verbatim}
📝 Checking: ./bad-style.md
"was decided by me" may be passive voice
"kind of" is vague and unnecessary
"really" is a weasel word
"quite" is a weasel word
❌ Some files have style issues detected by write-good.
Error: Process completed with exit code 1.
\end{verbatim}

CI 任务失败，提交被标记为不通过。这表明自定义 Action 成功捕获了写作中的不良风格，并阻止了低质量文档的合入。
具体操作如图\ref{构建github action -1},图\ref{构建github action -2},图\ref{构建github action -3}和图\ref{构建github action -结果}。
\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-22 192547.png}
	\caption{构建github action -1}
	\label{构建github action -1}
\end{figure}

\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-22 192554.png}
	\caption{构建github action -2}
	\label{构建github action -2}
\end{figure}

\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-22 192558.png}
	\caption{构建github action -3}
	\label{构建github action -3}
\end{figure}

\begin{figure}[htb]
	\centering
	\includegraphics[width=\linewidth]{assets/images/屏幕截图 2025-09-22 192520.png}
	\caption{构建github action -结果}
	\label{构建github action -结果}
\end{figure}